package com.imagefake.filter;

import com.alibaba.fastjson2.JSON;
import com.imagefake.entity.SysUser;
import com.imagefake.util.JwtRedisUtil;
import com.imagefake.util.JwtUtils;
import com.imagefake.vo.HttpResult;
import com.imagefake.vo.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * JwtCheckFilter过滤器，作用是检查请求是否传来JWT，检查JWT是否存在于redis,以及检验JWT的真实性
 * 如果通过检查，将用户信息存入到Security上下文
 *
 * @author wj
 */
@Component
@Slf4j
public class JwtCheckFilter extends OncePerRequestFilter {
    @Resource
    private JwtRedisUtil jwtRedisUtil;

    @Resource
    private JwtUtils jwtUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取请求RUI
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        //访问登录页面，放行
        if (    "/login".equals(requestURI)
                || "/web/login".equals(requestURI)
                || "/doLogin".equals(requestURI)
                || "/register".equals(requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 在jwt认证之前有没有其他方式认证过
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            filterChain.doFilter(request, response);
            return;
        }

        //获取请求中的JWT
        String authorization = request.getHeader("Authorization");
        String jwtToken = null;
        if (authorization != null) {
            jwtToken = authorization.replace("Bearer ", "");
        }

        //如果JWT为空,返回用户未登录
        if (!StringUtils.hasText(jwtToken)) {
            printFront(response, "用户未登录");
            return;
        }
        //验证JWT是否存入Redis中
        if (!StringUtils.hasText(jwtRedisUtil.getJwt(jwtToken))) {
            printFront(response, "用户已退出，请重新登录");
            jwtRedisUtil.deleteJwt(jwtToken);
            return;
        }
        //验证JWT是否有效
        if (!jwtUtils.verifyToken(jwtToken)) {
            //JWT无效
            printFront(response, "jwtToken 已过期");
            return;
        }
        //JWT有效,获取JWT内的信息
        String userInfo = jwtUtils.getUserInfo(jwtToken);
        List<String> userAuth = jwtUtils.getUserAuth(jwtToken);
        SysUser sysUser = JSON.parseObject(userInfo, SysUser.class);
        SecurityUser securityUser = new SecurityUser(sysUser);
        List<SimpleGrantedAuthority> authList = userAuth
                .stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        securityUser.setSimpleGrantedAuthorities(authList);

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(securityUser, null, authList);
        //通过安全上下文设置认证信息
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

        //放行请求
        filterChain.doFilter(request, response);
    }

    /**
     * 向前端响应异常的方法
     * @param response response对象
     * @param message 异常信息
     * @throws IOException 获取writer时发生的异常
     */
    private void printFront(HttpServletResponse response, String message) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        HttpResult httpResult = HttpResult.builder().code(-1).msg(message).build();
        response.getWriter().print(JSON.toJSONString(httpResult));
        response.getWriter().flush();
    }
}
